#ifndef TOKENMANAGER_H
#define TOKENMANAGER_H
#include "Token.h"
#include "python3/PythonGrammar30Constants.h"

class CharStream;

class TokenManager
{
public:
    TokenManager()
    {
        memset(&indentation, 0, sizeof(int));
        level = 0;
        levelBeforeEof = 0;
        parens = 0;
        indent = -1;
    }
    virtual ~TokenManager()
    {
        specialTokens.clear();
        //qDeleteAll(specialTokens);
    }
    static const int CUSTOM_NOT_CREATED = 0;
    static const int CUSTOM_CREATED_NOT_PARENS = 1;
    static const int CUSTOM_CREATED_WAS_PARENS = 2;
public:
    virtual TokenPtr getNextToken() = 0;
    virtual CharStream* getInputStream() = 0;
    int addCustom(const TokenPtr& curr, const QString & token)
    {
        int id = getTokenToId().value(token, -1);
        if (id != -1){
            createFromAndSetAsNext(curr, id, token);
            int ret = CUSTOM_CREATED_NOT_PARENS;
            if (id == RPAREN || id == RBRACKET || id == RBRACE) {
                parens--;
                ret = CUSTOM_CREATED_WAS_PARENS;
            } else if(id == LPAREN) {
                parens++;
                ret = CUSTOM_CREATED_WAS_PARENS;
            }
            return ret;
        }
        return CUSTOM_NOT_CREATED;
    }
    virtual void indenting(int i) = 0;
    virtual int getCurrentLineIndentation() = 0;
    virtual int getLastIndentation() = 0;
    virtual int getCurLexState() = 0;
    void lexicalError(bool EOFSeen, int curLexState, int errorLine, int errorColumn, const QString& errorAfter, QChar curChar)
    {
        qDebug() << "Lexical error at" << errorLine << ":" << errorColumn << "after" << errorAfter;
        //throw QString("Lexical error");
    }

    TokenPtr createFrom(const TokenPtr& previous, int kind, const QString image)
    {
        TokenPtr t(Token::newToken(kind, image));
        t->beginLine = previous->beginLine;
        t->endLine = previous->endLine;
        t->beginColumn = previous->beginColumn;
        t->endColumn = previous->endColumn;
        t->startPos = previous->startPos;
        t->endPos = previous->endPos;
        t->specialToken.clear();
        t->next.clear();
        return t;
    }
protected:
    TokenPtr createFromAndSetAsNext(const TokenPtr& previous, int kind, const QString & image)
    {
        TokenPtr t(createFrom(previous, kind, image));
        TokenPtr oldNext = previous->next;
        previous->next = t;
        t->next = oldNext;
        return t;
    }

    TokenPtr addDedent(const TokenPtr& previous)
    {
        return createFromAndSetAsNext(previous, DEDENT, "<DEDENT>");
    }

    void CommonTokenAction(const TokenPtr initial)
    {
        TokenPtr t = initial;

        int i = specialTokens.length();
        while (t->specialToken) {
            specialTokens.insert(i, t->specialToken);
            t = t->specialToken;
        }

        //Now, we must check the actual token here for EOF.
        t = initial;

        //This is the place we check if we have to add dedents so that the parsing ends 'gracefully' when
        //we find and EOF.
        if (t->kind == _EOF) {
            //Store it because if we backtrack we have to restore it!!
            levelBeforeEof = level;
            if (getCurLexState() == DEFAULT) {
                t->kind = NEWLINE;
            } else {
                t->kind = DEDENT;
                if (level >= 0)
                    level--;
            }
            while (level >= 0) {
                level--;
                t = addDedent(t);
            }
            t->kind = _EOF;
            t->image = "<EOF>";
        }
    }

private:
    QMap<QString, int> getTokenToId()
    {
        if(tokenToIdCache.count() == 0){
            tokenToIdCache[")"] = RPAREN;
            tokenToIdCache["]"] = RBRACKET;
            tokenToIdCache["}"] = RBRACE;

            tokenToIdCache[":"] = COLON;
            tokenToIdCache["("] = LPAREN;
            tokenToIdCache[","] = COMMA;
        }
        return tokenToIdCache;
    }



public:
    QList<ObjectPtr> specialTokens;
    int indentation[20];
    int level;
    int levelBeforeEof;
    int parens;
    int indent;

private:
    QMap<QString, int> tokenToIdCache;
};

#endif
